package com.wrupple.muba.security.client.session;

import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;

/**
 * @author japi
 */
public class SessionPropagator {

	private final class SessionRequestCallback implements RequestCallback {
		public void onError(com.google.gwt.http.client.Request request, Throwable e) {
			GWT.log("login request failed, calling failure Handler", e);
			setStatus(SessionPropagator.STATUS_DONE);
			manejador.requestFailed(request, e);
			if (finalizer != null) {
				DeferredCommand.addCommand(finalizer);
			}
		}

		public void onResponseReceived(Request request, Response response) {
			GWT.log(this.getClass() + "login request succeded with status " + response.getStatusCode(), null);
			setStatus(SessionPropagator.STATUS_DONE);
			if (manejador.loginDecision(request, response)) {
				GWT.log(this.getClass() + "authentication granted", null);
				manejador.loginSuccesful(request, response);
			} else {
				GWT.log(this.getClass() + "authentication failed", null);
				manejador.loginFailed(request, response);
			}
			if (finalizer != null) {
				DeferredCommand.addCommand(finalizer);
			}
		}
	}

	private int status;
	private SessionSpreadHandler manejador;
	/**
	 * elemento que se disparará una vez que la sesión haya sido replicada y
	 * confirmada exitosamente
	 */
	private Command finalizer;
	/**
	 * Aún no se ha puesto la propiedad credentials y el login_service
	 */
	public static final int STATUS_PENDING = 0;
	/**
	 * Credentials y login_service están seteados
	 */
	public static final int STATUS_PREPARED = 1;
	/**
	 * El request fue enviado y se espera respuesta
	 */
	public static final int STATUS_WAITING = 2;
	/**
	 * La petición ha sido hecha
	 */
	public static final int STATUS_DONE = 3;

	public SessionPropagator() {
		super();
		this.setStatus(SessionPropagator.STATUS_PENDING);
	}

	public SessionPropagator(SessionSpreadHandler manejador) {
		super();
		this.manejador = manejador;
		this.setStatus(SessionPropagator.STATUS_PENDING);
	}

	public int getStatus() {
		return status;
	}

	private void setStatus(int status) {
		this.status = status;
	}

	private boolean isPrepared() {
		return this.manejador != null && this.finalizer != null;
	}

	/**
	 * @return the trigger that will be fired once the session has been
	 *         succesfully replicated to the server
	 */
	public Command getFinalizer() {
		return finalizer;
	}

	/**
	 * 
	 * @param interfaz
	 *            the trigger that will be fired once the session has been
	 *            succesfully replicated to the server
	 */
	public void setFinalizer(Command interfaz) {
		if (this.isPrepared())
			this.setStatus(SessionPropagator.STATUS_PREPARED);
		this.finalizer = interfaz;
	}

	public void setManejador(SessionSpreadHandler manejador) {
		if (this.isPrepared())
			this.setStatus(SessionPropagator.STATUS_PREPARED);
		this.manejador = manejador;
	}

	public SessionSpreadHandler getManejador() {
		return manejador;
	}

	public void hacerLogin() {
		GWT.log(this.getClass() + "Starting login process", null);
		GWT.log(this.getClass() + "Preparing Handler", null);
		this.manejador.prepare();
		if (!this.isPrepared()) {
			GWT.log("Not Enough Parameters no do login");
			throw new IllegalStateException("no existen parámetros suficientes para hacer la petición");
		}
		GWT.log(this.getClass() + "Building login request with method " + this.manejador.getHttpMethod() + " and url "
				+ this.manejador.getLoginService(), null);
		RequestBuilder req = new RequestBuilder(this.manejador.getHttpMethod(), this.manejador.getLoginService());
		GWT.log(this.getClass() + "preparing Handler", null);
		this.manejador.prepare(req);
		req.setCallback(getRequestCallback());
		GWT.log(this.getClass() + "Request callback set.", null);
		doRequest(req);
		return;
	}

	private RequestCallback getRequestCallback() {
		GWT.log(this.getClass() + "Building new Request Callback Object.", null);
		return new SessionRequestCallback();
	}

	private void doRequest(RequestBuilder req) {
		try {
			GWT.log(this.getClass() + "sending login request", null);
			req.send();
			this.setStatus(SessionPropagator.STATUS_WAITING);
		} catch (RequestException e) {
			GWT.log(this.getClass() + "login request could not be sent ", e);
			manejador.requestFailed(null, e);
			this.setStatus(SessionPropagator.STATUS_DONE);
		}

	}

}
